package com.vanguard.codec;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Title: 客户端解码器 解码方式为 定长
 * @Description: 解码Server的响应报文给Client
 * @Author: vanguard
 * @Version: 1.0
 * @Date: 18/12/27
 */
public class ClientFixedLengthDecode extends CumulativeProtocolDecoder {
    private final String charset;
    /**
     * 报文长度字段的长度
     */
    private final int dataLengthFieldValueLength = 6;


    private final boolean isDataLengthFieldValueContainSelf = false;
    //报文长度字段从第几位开始
    private final int dataLengthFieldStartIndex = 0;
    
    private final static Logger logger = LoggerFactory.getLogger(ClientFixedLengthDecode.class);
    
    //注意这里使用了Mina自带的AttributeKey类来定义保存在IoSession中对象的键值,其可有效防止键值重复  
    private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
    
    public ClientFixedLengthDecode(String charset){
        this.charset = charset;
    }  
    private Context getContext(IoSession session){
        Context context = (Context)session.getAttribute(CONTEXT);  
        if(null == context){  
            context = new Context();  
            session.setAttribute(CONTEXT, context);  
        }  
        return context;  
    }  
    @Override  
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        Context ctx = this.getContext(session);  
        IoBuffer buffer = ctx.innerBuffer;
        int messageCount = ctx.getMessageCount();  
        
        //报文长度字段以及其前面的部分 总共的长度
        int dataLengthFieldAndBeforeLength = dataLengthFieldStartIndex + dataLengthFieldValueLength;

        //判断position和limit之间是否有元素
        while(in.hasRemaining()){
            //get()读取buffer的position的字节,然后position+1
            buffer.put(in.get());

            //约定:报文的前dataLengthFieldValueLength个字符表示报文总长度,不足dataLengthFieldValueLength位则左侧补0
            if(messageCount++ == (dataLengthFieldAndBeforeLength-1)){
                buffer.flip();
                
                byte[] dataLengthFieldValueLengthByte = new byte[dataLengthFieldAndBeforeLength];
                buffer.get(dataLengthFieldValueLengthByte); 
                String dataLengthFieldValueLengthString = new String(dataLengthFieldValueLengthByte, charset);
                String messageLengthString = dataLengthFieldValueLengthString.substring(dataLengthFieldStartIndex);
                
                try{
                    ctx.setMessageLength(Integer.parseInt(messageLengthString.trim()));  
                }catch(NumberFormatException e){
                	logger.error("报文长度转换出错！messageLengthString：" + messageLengthString, e);
                    ctx.setMessageLength(in.limit());  
                }
                //让两个IoBuffer的limit相等
                buffer.limit(in.limit());
            }  
        }  
        ctx.setMessageCount(messageCount);  
        
        int messageTotalLength;//报文总体长度
        //报文长度字段表示总体包长
        if(isDataLengthFieldValueContainSelf){
        	messageTotalLength = ctx.getMessageLength();
            //报文总长度小于报文长度字段长
        	if(messageTotalLength<dataLengthFieldValueLength){
        		logger.error("报文解析错误。。。");
        	}
        }else {//报文长度字段表示 总体包长-报文长度字段长
        	messageTotalLength = ctx.getMessageLength() + dataLengthFieldValueLength + dataLengthFieldStartIndex;
        }

        //传过来的数据长度可能大于报文长度
        if(messageTotalLength <= buffer.position()){
            buffer.flip();  
            byte[] message = new byte[messageTotalLength];  
            buffer.get(message);  
            out.write(new String(message, charset));  
            ctx.reset();  
            return true;
            //传过来的数据小余报文长度
        }else{
            return false;  
        }
        
    }
    private class Context{
        /**
         * 用于累积数据的IoBuffer
         */
        private final IoBuffer innerBuffer;
        /**
         * 记录已读取的报文字节数
         */
        private int messageCount;
        /**
         * 记录已读取的报文头标识的报文长度
         */
        private int messageLength;
        public Context(){
            innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
        }
        public int getMessageCount() {
            return messageCount;
        }
        public void setMessageCount(int messageCount) {
            this.messageCount = messageCount;
        }
        public int getMessageLength() {
            return messageLength;
        }
        public void setMessageLength(int messageLength) {
            this.messageLength = messageLength;
        }
        public void reset(){
            this.innerBuffer.clear();
            this.messageCount = 0;
            this.messageLength = 0;
        }
    }
}
